스레드 (컴퓨팅)
"오늘의AI위키"의 AI를 통해 더욱 풍부하고 폭넓은 지식 경험을 누리세요.
1. 개요
스레드는 컴퓨터 프로그램 내에서 실행 흐름을 나타내는 개념으로, 1967년 IBM의 OS/360에서 처음 등장했다. 멀티스레딩은 하나의 프로세스 내에서 여러 스레드가 동시에 실행되도록 하여, 응답성을 높이고 병렬 처리를 가능하게 한다. 스레드는 커널 레벨 또는 사용자 레벨에서 지원되며, 스레드 간의 컨텍스트 전환 속도가 프로세스보다 빠르다는 장점이 있다. 하지만, 동기화 문제, 테스트의 어려움, 동기화 비용 증가 등의 단점도 존재한다. 프로그래밍 언어는 스레딩을 다양한 방식으로 지원하며, 싱글 스레드 방식과 멀티스레드 방식 중 선택하여 프로그램을 개발할 수 있다.
스레드는 1967년 IBM의 일괄 처리 운영 체제인 OS/360에서 "태스크"라는 이름으로 처음 등장했다. 이는 사용자에게 가변 태스크 수의 다중 프로그래밍(MVT)을 포함한 세 가지 구성의 OS/360 제어 시스템을 제공했다.[3] 1966년 Saltzer는 "스레드"라는 용어를 빅터 A. 비소츠키의 것으로 본다.[3]
멀티프로세스와 멀티스레드는 모두 여러 흐름이 동시에 진행된다는 공통점을 가진다. 하지만 멀티프로세스에서 각 프로세스는 독립적으로 실행되며 각각 별개의 메모리를 차지하는 반면, 멀티스레드는 프로세스 내의 메모리를 공유하여 사용할 수 있다. 또한 프로세스 간의 전환 속도보다 스레드 간의 전환 속도가 빠르다.[5]
스레드는 지원 주체에 따라 사용자 레벨 스레드와 커널 레벨 스레드로 나눌 수 있다.
2. 역사
소프트웨어 애플리케이션에서 스레드의 사용은 CPU가 여러 코어를 활용하기 시작한 2000년대 초반에 더 흔해졌다. 성능상의 이점을 위해 여러 코어를 활용하려는 애플리케이션은 여러 코어를 사용하기 위해 동시성을 사용해야 했다.[4]
3. 프로세스와 스레드의 비교
멀티스레드의 장점은 CPU가 여러 개일 경우, 각 CPU가 스레드 하나씩을 담당하는 방법으로 속도를 높일 수 있다는 것이다. 이러한 시스템에서는 여러 스레드가 실제 시간상으로 동시에 수행될 수 있다.
멀티스레드의 단점은 각 스레드의 실행 순서를 예측하기 어렵다는 것이다. 예를 들어, 두 스레드가 공유 변수 i의 값을 1 증가시키는 경우, 다음과 같은 순서로 실행될 수 있다.스레드 동작 i의 값 스레드 1의 레지스터 스레드 2의 레지스터 스레드 1 i의 값을 레지스터에 저장 0 0 스레드 1 레지스터 값을 1 증가 0 1 스레드 1 i에 값 저장 1 1 스레드 2 i의 값을 레지스터에 저장 1 1 1 스레드 2 레지스터 값을 1 증가 1 1 2 스레드 2 i에 값 저장 2 1 2
위와 같이 실행되면 i는 2가 증가한다. 하지만 다음과 같이 실행될 수도 있다.스레드 동작 i의 값 스레드 1의 레지스터 스레드 2의 레지스터 스레드 1 i의 값을 레지스터에 저장 0 0 스레드 2 i의 값을 레지스터에 저장 0 0 0 스레드 1 레지스터 값을 1 증가 0 1 0 스레드 2 레지스터 값을 1 증가 0 1 1 스레드 1 i에 값 저장 1 1 1 스레드 2 i에 값 저장 1 1 1
이 경우 i는 1만 증가하며, 이는 원래 의도와 다를 수 있다. 이러한 문제는 스레드 실행 조건에 따라 결과가 달라지므로, 오류 발생 시 원인 파악이 어렵다. 이를 경쟁 조건이라 하며, 세마포어와 같은 방법을 통해 공유 데이터에 접근하는 스레드 개수를 제한하여 해결할 수 있다.
스레드는 다음과 같은 여러 면에서 전통적인 컴퓨터 멀티태스킹 운영체제 프로세스 (컴퓨팅)와 다르다.
윈도우 NT 및 OS/2와 같은 시스템은 '저렴한' 스레드와 '비싼' 프로세스를 가지고 있다고 한다. 다른 운영 체제에서는 일부 아키텍처(특히 x86)에서 주소 공간 스위치 비용을 제외하면 큰 차이가 없으며, 이로 인해 TLB 플러시가 발생한다.
스레드와 프로세스의 장단점은 다음과 같다.4. 스레드의 종류
프로세스는 커널 스케줄링의 "무거운" 단위이며, 생성, 소멸, 전환에 비교적 많은 비용이 든다. 프로세스는 운영 체제에서 할당한 자원을 소유하며, 프로세스 격리에 의해 ''격리''된다. 각 프로세스 내에는 최소한 하나 이상의 커널 스레드가 존재하며, 프로세스 내의 여러 커널 스레드는 동일한 메모리 및 파일 자원을 공유한다. 커널 스레드는 운영 체제의 프로세스 스케줄러가 선점형인 경우 선점형 멀티태스킹되며, 자원을 거의 소유하지 않으므로 생성 및 제거, 스레드 전환 비용이 비교적 저렴하다.
4. 1. 사용자 레벨 스레드 (User-Level Thread)
사용자 스레드는 커널 영역 상위에서 지원되며, 일반적으로 사용자 레벨의 라이브러리를 통해 구현된다. 이 라이브러리는 스레드의 생성 및 스케줄링 등에 관한 관리 기능을 제공한다.[6] 사용자 스레드는 동일한 메모리 영역에서 생성 및 관리되므로 속도가 빠르다. 하지만 여러 사용자 스레드 중 하나가 시스템 호출 등으로 중단되면, 커널이 프로세스 내부의 스레드를 인식하지 못하고 해당 프로세스를 대기 상태로 전환시키기 때문에 나머지 모든 스레드도 중단되는 단점이 있다.[6]
사용자 스레드는 보통 사용자 공간에서 구현되므로, 동일 프로세스 내에서 사용자 스레드 간의 컨텍스트 전환은 매우 효율적이다. 이는 커널과의 상호 작용이 전혀 필요하지 않기 때문이다. 컨텍스트 전환은 현재 실행 중인 사용자 스레드가 사용하는 CPU 레지스터를 로컬로 저장한 다음, 실행될 사용자 스레드가 필요로 하는 레지스터를 로드하여 수행할 수 있다. 스케줄링이 사용자 공간에서 이루어지기 때문에 프로그램의 워크로드 요구 사항에 맞게 스케줄링 정책을 더 쉽게 조정할 수 있다.
그러나 사용자 스레드에서 차단 시스템 호출을 사용하는 것은 문제가 될 수 있다. 사용자 스레드가 차단되는 시스템 호출을 수행하면, 시스템 호출이 반환될 때까지 프로세스의 다른 사용자 스레드는 실행할 수 없다. 이 문제의 전형적인 예는 I/O를 수행할 때이다. 대부분의 프로그램은 I/O를 동기적으로 수행하도록 작성된다. I/O 작업이 시작되면 시스템 호출이 이루어지고, I/O 작업이 완료될 때까지 반환되지 않는다. 그 동안 전체 프로세스는 커널에 의해 "차단"되어 실행할 수 없으므로, 동일한 프로세스의 다른 사용자 스레드가 실행되지 못하게 된다.[6]
이 문제에 대한 일반적인 해결책은 내부적으로 논블로킹 I/O를 사용하고, I/O 작업이 진행되는 동안 다른 사용자 스레드를 스케줄링하여 전체 프로세스가 아닌 호출 스레드를 차단하는 인터페이스를 구현하는 I/O API를 제공하는 것이다. 다른 차단 시스템 호출에 대해서도 유사한 해결책을 제공할 수 있다. 또는 프로그램은 동기 I/O 또는 다른 차단 시스템 호출의 사용을 피하도록 작성될 수 있다(특히, 람다 연속 및/또는 비동기/await 기본 기능을 포함하는 논블로킹 I/O 사용).[6]
사용자 공간에서 구현된 스레드 기법을 '''사용자 스레드'''라고 하며, 특히 가상 머신에서 실행되는 것을 그린 스레드라고 부른다. 사용자 스레드의 전환은 해당 프로세스가 사용자 공간에서 동작하는 동안 라이브러리 내의 스레드 스케줄러가 수행한다. 이는 오버헤드가 매우 작고, 구현이 간단하다는 장점이 있다. 그러나 하나의 프로세스 내의 여러 스레드는 항상 하나만 동작하게 되어, 멀티프로세서 시스템의 이점을 얻을 수 없다. 또한, 어떤 스레드가 커널 내에서 입출력 대기 상태로 들어가면, 모든 스레드가 입출력 대기 상태가 된다. 즉, 사용자 스레드는 프로그래밍 기법으로서만 의미가 있으며, 성능 향상에는 기여하지 못한다.
4. 2. 커널 레벨 스레드 (Kernel-Level Thread)
커널 스레드는 운영체제가 지원하는 스레드 기능으로 구현되며, 커널이 스레드의 생성 및 스케줄링 등을 관리한다.[19] 스레드가 시스템 호출 등으로 중단되더라도, 커널은 프로세스 내의 다른 스레드를 중단시키지 않고 계속 실행시켜준다. 다중처리기 환경에서 커널은 여러 개의 스레드를 각각 다른 처리기에 할당할 수 있다. 다만, 사용자 스레드에 비해 생성 및 관리하는 것이 느리다.
커널 스레드는 커널 스케줄링의 "경량" 단위이다. 각 프로세스 내에 최소한 하나의 커널 스레드가 존재한다. 프로세스 내에 여러 개의 커널 스레드가 존재하면 동일한 메모리 및 파일 자원을 공유한다. 커널 스레드는 운영 체제의 프로세스 스케줄러가 선점형인 경우 선점형 멀티태스킹된다. 커널 스레드는 스택, 레지스터 (프로그램 카운터 포함)의 복사본, 스레드 로컬 저장소 (있는 경우)를 제외한 자원을 소유하지 않으므로 생성 및 제거 비용이 비교적 저렴하다. 스레드 전환 역시 비교적 저렴하다. 컨텍스트 전환(레지스터 및 스택 포인터 저장 및 복원)이 필요하지만 가상 메모리를 변경하지 않으므로 캐시에 친화적이다(TLB 유효 상태 유지). 커널은 CPU의 각 코어에 하나 이상의 소프트웨어 스레드를 할당할 수 있으며 (멀티스레딩 지원에 따라 자체적으로 여러 소프트웨어 스레드를 할당할 수 있음) 차단된 스레드를 스왑아웃할 수 있다. 그러나 커널 스레드를 스왑하는 데는 사용자 스레드보다 훨씬 더 오랜 시간이 걸린다.
커널 공간에서 구현된 스레드 기법을 '''커널 스레드'''라고 부른다. 커널 스레드의 전환은 커널이 수행하기 때문에, 멀티프로세서 시스템이라면 같은 프로세스 내의 여러 스레드를 병렬로 실행할 수 있으며, 그 중 하나의 스레드가 슬립 상태가 되더라도 다른 스레드는 처리를 계속할 수 있다. 그러나 커널 스레드는 단순히 사용자 공간 등의 자원을 공유하고 있을 뿐이다. 또한, 커널이 모든 스레드를 관리하기 때문에 생성 가능한 스레드 수의 제한이 커진다.
5. 스레드 데이터
스레드는 고유한 스레드 ID, 프로그램 카운터, 레지스터 집합, 스택을 가진다. 코드, 데이터, 파일 등 기타 자원은 프로세스 내의 다른 스레드와 공유한다.[19]
하나의 스레드에만 연관된 데이터가 필요한 경우가 있는데, 이를 '''스레드 특정 데이터'''(Thread-Specific Data, TSD)라고 한다. 멀티스레드 프로그래밍 환경에서 모든 스레드는 프로세스의 데이터를 공유하지만, 특별한 경우에는 개별 스레드만의 자료 공간이 필요하다. 예를 들어, 여러 개의 트랜잭션을 스레드로 처리할 경우, 각각의 트랜잭션 ID를 기억하고 있어야 하는데, 이때 TSD가 필요하다. TSD는 여러 스레드 라이브러리들이 지원하는 기능 중 하나이다.[19]
6. 스레드 모델
컴퓨터에서 병행 컴퓨팅/병렬 컴퓨팅 또는 멀티태스킹과 같이 여러 처리를 동시에 실행하려면 컴퓨터와 그 위에서 작동하는 운영 체제(OS)가 프로세서(CPU) 시간을 개별 처리에 적절하게 분배하고 스케줄링하는 기능을 지원해야 한다. 이때, 동시에 실행할 부분을 지정할 수 있는 처리 분할 단위로, '''스레드'''와 '''프로세스'''가 있다.
프로세스는 CPU나 메인 메모리 상의 주소 공간 등 계산 자원(리소스)을 할당받아 독립적으로 작동한다. 일반적으로 시스템의 안정성과 안전성을 위해, 각 프로세스는 OS에 의해 가상화·분리된 가상 주소 공간을 사용한다.[19] 프로세스마다 메모리 공간이 독립되어 있어, 다른 프로세스의 메모리에 직접 접근하는 것은 불가능하다. 그러나 이러한 독립된 메모리 공간이 불필요한 경우에는 메모리 이용 효율이 나빠진다.
이러한 문제를 해결하기 위해, 단일 공간 내의 메모리를 공유하면서 여러 처리를 수행하는 "공유 메모리 방식"이 사용될 수 있다. 이를 가능하게 하는 것이 스레드이다. 스레드를 사용하면 동일 프로세스 내의 여러 스레드를 동일 메모리 공간에서 실행할 수 있어, 메모리 소비량을 줄일 수 있다.
멀티태스킹 OS에서 하나의 태스크는 하나 이상의 프로세스로 구성되며, 하나의 프로세스는 하나 이상의 스레드로 구성된다. 그러나 이 관계는 환경에 따라 다를 수 있다.
멀티스레드 처리 프로그래밍에서는 같은 데이터를 여러 스레드가 동시에 덮어쓰는 것에 주의하고, 상호 배제를 수행해야 한다. 공유 라이브러리 사용 시에는 해당 공유 라이브러리가 스레드 안전(재진입 가능)인지 확인해야 한다. 또한, 여러 스레드가 협력하여 작동할 때, 서로의 처리 완료를 기다리면서 데드락(deadlock) 상태에 빠지지 않도록 주의해야 한다.
어떤 처리를 단일 스레드만 사용하여 작동시키는 환경 또는 기법을 '''싱글 스레드'''라고 한다. 반대로, 여러 스레드가 동시에 작동하는 것을 '''멀티스레드'''라고 한다. 프로그램 시작 시에는 메인 스레드가 작동하며, 필요에 따라 기타 처리를 하는 스레드를 만들어 실행할 수 있다.
기본적인 모델로, 하나의 CPU 코어가 특정 시점에 동시에 실행하는 것은 인터럽트 처리를 포함하여 하나의 스레드, 하나의 프로세스, 하나의 태스크이다. 동시 멀티스레딩(SMT) 기술을 통해, 물리 CPU 코어로는 2개 이상의 스레드를 동시에 실행하는 것이 가능하지만, 애플리케이션 소프트웨어 관점에서는 SMT에 의해 생겨나는 논리 CPU 코어가 특정 시점에 실행할 수 있는 스레드는 하나이다.
스레드 모델에는 다음과 같은 세 가지 주요 모델이 있다.
- 1:1 모델 (Kernel-Level Threading): 사용자 스레드가 커널의 스케줄 가능한 개체와 1:1로 대응되는 가장 단순한 구현 방식이다.
- M:1 모델 (User-Level Threading): 모든 애플리케이션 수준의 스레드가 하나의 커널 수준으로 스케줄링된 개체에 매핑되는 방식이다. 컨텍스트 전환이 빠르지만, 멀티스레드 프로세서 또는 멀티 프로세서 컴퓨터의 하드웨어 가속을 활용할 수 없다.
- M:N 모델 (Hybrid Threading): M개의 사용자 레벨 스레드를 N개의 커널 개체(가상 프로세서)에 매핑하는 방식이다. 커널 스레드와 사용자 스레드 사이의 절충안이지만, 구현이 복잡하고 우선 순위 역전 가능성이 있다.
6. 1. 1:1 모델 (Kernel-Level Threading)
OS/2와 Win32는 처음부터 이 방식을 사용했으며, 리눅스에서는 GNU C 라이브러리가 이 방식을 구현한다(NPTL 또는 구 버전인 LinuxThreads를 통해).[9] 이 방식은 Solaris, NetBSD, FreeBSD, macOS, 그리고 iOS에서도 사용된다.[9] 사용자 스레드가 커널의 스케줄 가능한 개체와 1:1로 대응되는 방식[9]은 가장 단순한 스레딩 구현 방식이다.6. 2. M:1 모델 (User-Level Threading)
M:1 모델은 모든 애플리케이션 수준의 스레드가 하나의 커널 수준으로 스케줄링된 개체에 매핑되는 방식이다.[9] 커널은 애플리케이션 스레드에 대해 알지 못한다. 이러한 접근 방식을 사용하면 컨텍스트 전환을 매우 빠르게 수행할 수 있으며, 스레딩을 지원하지 않는 간단한 커널에서도 구현할 수 있다. 그러나 멀티스레드 프로세서 또는 멀티 프로세서 컴퓨터의 하드웨어 가속을 활용할 수 없다는 단점이 있다. 동시에 스케줄링되는 스레드가 하나 이상 존재하지 않기 때문이다.[9] 예를 들어, 스레드 중 하나가 I/O 요청을 실행해야 하는 경우 전체 프로세스가 차단되어 스레딩의 이점을 사용할 수 없다. GNU Portable Threads는 State Threads와 마찬가지로 사용자 수준 스레딩을 사용한다.6. 3. M:N 모델 (Hybrid Threading)
M:N 모델은 M개의 사용자 레벨 스레드를 N개의 커널 개체(가상 프로세서)에 매핑하는 방식이다[9]. 커널 스레드(1:1)와 사용자 스레드(N:1) 사이의 절충안이라고 할 수 있다.M:N 스레딩 시스템은 커널과 사용자 공간 코드 모두를 변경해야 하기 때문에, 일반적으로 커널 스레드나 사용자 스레드보다 구현하기가 더 복잡하다. M:N 구현에서 스레딩 라이브러리는 사용 가능한 스케줄링 가능한 개체에서 사용자 스레드를 스케줄링하는 역할을 담당한다. 이는 시스템 호출을 피하므로 스레드의 컨텍스트 전환 속도를 매우 빠르게 만든다.
하지만, M:N 모델은 복잡성을 증가시키고 우선 순위 역전 가능성을 높인다. 또한, 사용자 공간 스케줄러와 커널 스케줄러 사이에 광범위하고 비용이 많이 드는 조율 없이는 최적의 스케줄링이 이루어지지 않을 수 있다.
7. 스케줄링
운영 체제는 스레드를 선점형 또는 협력적으로 스케줄링한다. 다중 사용자 운영 체제는 일반적으로 컨텍스트 전환을 통해 실행 시간에 대한 더 세밀한 제어를 위해 선점형 멀티스레딩을 선호한다. 그러나 선점형 스케줄링은 잠금 컨보이, 우선 순위 역전 등의 부작용을 일으킬 수 있다. 반대로, 협력적 멀티스레딩은 스레드가 실행 제어를 포기하는 데 의존하므로 스레드가 완료될 때까지 실행되도록 보장한다. 이는 협력적으로 멀티태스킹된 스레드가 차단되어 리소스를 대기하거나, 집중적인 계산 중에 실행 제어를 양보하지 않아 다른 스레드를 기아 상태로 만들 경우 문제를 일으킬 수 있다.
단일 프로세서 시스템은 시간 분할 방식을 사용하여 멀티스레딩을 구현하는데, 중앙 처리 장치(CPU)가 서로 다른 ''소프트웨어 스레드'' 간에 전환한다. 이 컨텍스트 스위치는 사용자가 스레드나 작업을 병렬로 실행하는 것으로 인식할 수 있을 만큼 충분히 자주 발생한다(서버/데스크톱 운영 체제의 경우, 다른 스레드가 대기 중일 때 스레드의 최대 시간 분할은 100~200ms). 멀티프로세서 또는 멀티코어 시스템에서는 여러 스레드가 병렬 컴퓨팅으로 실행될 수 있으며, 모든 프로세서 또는 코어가 별도의 스레드를 동시에 실행한다. ''하드웨어 스레드''가 있는 프로세서 또는 코어에서는 별도의 하드웨어 스레드가 별도의 소프트웨어 스레드를 동시에 실행할 수도 있다.
7. 1. 선점형 vs 협력적 스케줄링
운영 체제는 스레드를 선점형 또는 협력적으로 스케줄링한다. 다중 사용자 운영 체제는 일반적으로 컨텍스트 전환을 통해 실행 시간에 대한 더 세밀한 제어를 위해 선점형 멀티스레딩을 선호한다. 그러나 선점형 스케줄링은 프로그래머가 예상하지 못한 시점에 스레드를 컨텍스트 전환할 수 있으므로 잠금 컨보이, 우선 순위 역전 등의 부작용을 일으킬 수 있다. 반대로, 협력적 멀티스레딩은 스레드가 실행 제어를 포기하는 데 의존하므로 스레드가 완료될 때까지 실행되도록 보장한다. 이는 협력적으로 멀티태스킹된 스레드가 차단되어 리소스를 대기하거나, 집중적인 계산 중에 실행 제어를 양보하지 않아 다른 스레드를 기아 상태로 만들 경우 문제를 일으킬 수 있다.7. 2. 단일 vs 다중 프로세서 시스템
2000년대 초반까지 대부분의 데스크톱 컴퓨터는 하드웨어 스레드를 지원하지 않는 단일 코어 CPU를 사용했지만, 스레드 간 전환이 전체 프로세스 컨텍스트 스위치보다 일반적으로 더 빨랐기 때문에 이러한 컴퓨터에서도 스레드가 사용되었다. 2002년, 인텔은 펜티엄 4 프로세서에 ''하이퍼 스레딩''이라는 이름으로 동시 멀티스레딩 지원을 추가했으며, 2005년에는 듀얼 코어 펜티엄 D 프로세서를, AMD는 듀얼 코어 애슬론 64 X2 프로세서를 출시했다.단일 프로세서 시스템은 일반적으로 시간 분할 방식을 사용하여 멀티스레딩을 구현한다. 즉, 중앙 처리 장치(CPU)가 서로 다른 ''소프트웨어 스레드'' 간에 전환한다. 이 컨텍스트 스위치는 일반적으로 사용자가 스레드나 작업을 병렬로 실행하는 것으로 인식할 수 있을 만큼 충분히 자주 발생한다(인기 있는 서버/데스크톱 운영 체제의 경우, 다른 스레드가 대기 중일 때 스레드의 최대 시간 분할은 종종 100~200ms로 제한된다). 멀티프로세서 또는 멀티코어 시스템에서는 여러 스레드가 병렬 컴퓨팅으로 실행될 수 있으며, 모든 프로세서 또는 코어가 별도의 스레드를 동시에 실행한다. ''하드웨어 스레드''가 있는 프로세서 또는 코어에서는 별도의 하드웨어 스레드가 별도의 소프트웨어 스레드를 동시에 실행할 수도 있다.
8. 프로그래밍 언어 지원
C, Java, 파이썬 등 많은 프로그래밍 언어가 어떤 형태로든 스레딩을 지원한다. POSIX 스레드(Pthreads)는 스레드 구현을 위한 표준화된 인터페이스로, C 함수 라이브러리 호출 집합이다. OS 공급업체는 이 인터페이스를 자유롭게 구현할 수 있지만, 응용 프로그램 개발자는 여러 플랫폼에서 동일한 인터페이스를 사용할 수 있어야 한다. 리눅스를 포함한 대부분의 유닉스 플랫폼과 마이크로소프트 윈도우는 각각 자체적인 스레드 함수 집합을 가지고 있다.[23]
Java, 파이썬, .NET Framework 언어와 같은 일부 고급 프로그래밍 언어는 런타임에서 스레딩 구현의 플랫폼 특정 차이점을 추상화하여 개발자에게 스레딩을 제공한다. Cilk, OpenMP, 메시지 전달 인터페이스(MPI)와 같은 다른 프로그래밍 언어 및 언어 확장은 개발자로부터 동시성 및 스레딩의 개념을 완전히 추상화하려고 시도한다.
일부 인터프리터 방식 프로그래밍 언어는 전역 인터프리터 잠금(GIL) 때문에 스레딩과 동시성을 지원하지만, 스레드의 병렬 실행은 지원하지 않는다(예: 루비의 Ruby MRI, 파이썬의 CPython).
CUDA와 같은 데이터 병렬 컴퓨팅을 위해 설계된 프로그래밍 모델에서 스레드 배열은 ID만을 사용하여 컴퓨트 커널을 병렬로 실행한다.
Verilog와 같은 하드웨어 기술 언어는 매우 많은 수의 스레드를 지원하는 다른 스레딩 모델을 가지고 있다.
초창기 프로그래밍 언어에서는 스레드가 언어 사양에서 표준적으로 지원되지 않았다. 예를 들어, 마이크로소프트 윈도우에서는 윈도우 API 스레드, POSIX 준수 OS에서는 POSIX 스레드(Pthreads)와 같이 플랫폼 고유의 API를 사용해야 했다. 그러나 C++11 규격에서는 Boost C++ 라이브러리를 기반으로 한 스레드 라이브러리가 표준화되었고, C11 규격에서도 스레드 라이브러리가 표준으로 정의되었다.[23]
9. 싱글 스레드 vs 멀티스레드 프로그램
컴퓨터 프로그래밍에서 싱글 스레딩은 한 번에 하나의 명령을 처리하는 것이다.[11] 반면, 멀티스레딩은 하나의 프로세스 내에서 여러 개의 스레드가 존재하도록 허용하는 프로그래밍 및 실행 모델이다. 이러한 스레드들은 프로세스의 자원을 공유하지만 독립적으로 실행될 수 있다.[12] 멀티스레딩은 병렬 실행을 위해 하나의 프로세스에 적용될 수 있으며, 멀티스레딩 라이브러리는 함수를 통해 새 스레드를 생성하고, 데이터 동기화 기능을 제공한다.
컴퓨터에서 여러 처리를 동시에 실행하려면, OS가 CPU 시간을 적절히 분배하고 스케줄링해야 한다. 이때 처리 분할 단위로 스레드와 프로세스가 사용된다. 프로세스는 실행될 때 CPU나 메인 메모리 상의 주소 공간 등 계산 자원을 할당받는다. 각 프로세스는 독립적으로 작동하며, 일반적으로 OS에 의해 가상화된 가상 주소 공간을 사용한다.[19]
하지만, 공유 메모리 방식이 로직 구현이나 메모리 효율 면에서 유리한 경우가 있는데, 이를 가능하게 하는 것이 스레드이다. 멀티태스킹 OS에서 하나의 태스크는 하나 이상의 프로세스로 구성되며, 하나의 프로세스는 하나 이상의 스레드로 구성된다. 스레드를 사용하면 동일 프로세스 내의 여러 스레드를 동일 메모리 공간에서 실행할 수 있어 메모리 소비를 줄일 수 있다. 그러나 이 때문에 멀티스레드 프로그래밍에서는 상호 배제를 수행해야 하며, 공유 라이브러리 사용 시에는 스레드 안전한지 확인해야 한다.
어떤 처리를 단일 스레드만 사용하는 것을 싱글 스레드, 여러 스레드가 동시에 작동하는 것을 멀티스레드라고 한다. 프로그램 시작 시 메인 스레드가 작동하며, 필요에 따라 다른 스레드를 만들어 실행할 수 있다. 기본적으로 하나의 CPU 코어는 특정 시점에 하나의 스레드, 하나의 프로세스, 하나의 태스크를 실행한다. 동시 멀티스레딩 (SMT) 기술을 통해 2개 이상의 스레드를 동시에 실행할 수 있지만, 애플리케이션 소프트웨어 관점에서는 논리 CPU 코어가 특정 시점에 실행할 수 있는 스레드는 하나이다.
9. 1. 장점
멀티스레딩은 애플리케이션의 응답성을 향상시킨다. 단일 스레드 프로그램에서 메인 스레드가 오래 걸리는 작업을 처리하면 프로그램 전체가 멈춘 것처럼 보일 수 있지만, 멀티스레딩을 통해 오래 걸리는 작업을 별도의 작업자 스레드로 분리하면 메인 스레드는 사용자 입력에 계속 응답할 수 있다. 논블로킹 I/O나 유닉스 시그널을 사용하는 것과 유사한 효과를 얻을 수 있다.[13]또한, 멀티스레딩은 멀티코어 또는 멀티 CPU 시스템에서 데이터와 작업을 병렬로 처리하여 성능을 향상시킬 수 있다. 각 스레드가 서로 다른 코어에서 동시에 실행되거나 병렬로 실행될 수 있기 때문이다. CUDA나 OpenCL과 같은 GPU 컴퓨팅 환경에서는 수십에서 수백 개의 스레드가 데이터 병렬성을 통해 다중 코어에서 데이터를 병렬로 처리하는 다중 스레딩 모델을 사용한다. 이를 통해 시스템 활용도를 높이고 프로그램 실행 속도를 높일 수 있지만, 동기화 비용이 커지면 오히려 성능이 저하될 수 있다.
9. 2. 단점
멀티스레드의 단점은 여러 스레드가 동시에 실행될 때 어떤 스레드가 먼저 실행될지 알 수 없다는 것이다. 이로 인해 공유 자원에 접근할 때 문제가 발생할 수 있다.예를 들어, 두 스레드가 공유 변수 i의 값을 1 증가시키는 경우를 생각해 보자. 각 스레드는 다음 단계를 따른다.
1. 공유 변수 i의 값을 레지스터에 저장한다.
2. 레지스터의 값을 1 증가시킨다.
3. 변수 i에 그 값을 저장한다.
스레드 실행 순서에 따라 결과가 달라진다.
경쟁 조건 발생 예시
스레드 | 동작 | i의 값 | 스레드 1의 레지스터 | 스레드 2의 레지스터 |
---|---|---|---|---|
스레드 1 | i의 값을 레지스터에 저장 | 0 | 0 | |
스레드 1 | 레지스터 값을 1 증가 | 0 | 1 | |
스레드 1 | i에 값 저장 | 1 | 1 | |
스레드 2 | i의 값을 레지스터에 저장 | 1 | 1 | 1 |
스레드 2 | 레지스터 값을 1 증가 | 1 | 1 | 2 |
스레드 2 | i에 값 저장 | 2 | 1 | 2 |
위의 표는 스레드 1이 먼저 실행된 후 스레드 2가 실행될 때 i 값이 2 증가하는 정상적인 경우이다.
스레드 | 동작 | i의 값 | 스레드 1의 레지스터 | 스레드 2의 레지스터 |
---|---|---|---|---|
스레드 1 | i의 값을 레지스터에 저장 | 0 | 0 | |
스레드 2 | i의 값을 레지스터에 저장 | 0 | 0 | 0 |
스레드 1 | 레지스터 값을 1 증가 | 0 | 1 | 0 |
스레드 2 | 레지스터 값을 1 증가 | 0 | 1 | 1 |
스레드 1 | i에 값 저장 | 1 | 1 | 1 |
스레드 2 | i에 값 저장 | 1 | 1 | 1 |
위의 표는 스레드 1과 스레드 2가 번갈아 실행될 때 i 값이 1만 증가하는 비정상적인 경우(경쟁 조건 발생)이다. 이는 프로그램의 의도와 다를 수 있다.
이러한 문제를 경쟁 조건이라고 하며, 세마포어 등을 사용하여 공유 데이터에 접근하는 스레드 수를 제한하여 해결할 수 있다.
스레드는 프로세스와 비교했을 때 다음과 같은 특징을 갖는다.
- 스레드는 프로세스의 하위 집합이다.
- 스레드는 프로세스 상태, 컴퓨터 저장소, 자원 (컴퓨터 과학)을 공유한다.
- 스레드는 주소 공간을 공유한다.
- 스레드 간 컨텍스트 스위칭은 프로세스 간 컨텍스트 스위칭보다 빠르다.
스레드와 프로세스의 장단점 비교
- 스레드의 장점:
- ''낮은 자원 소비'': 프로세스보다 적은 자원으로 애플리케이션을 실행할 수 있다.
- ''간소화된 공유 및 통신'': 프로세스 간 통신 없이 데이터를 공유할 수 있다.
- 스레드의 단점:
- ''스레드 충돌 시 프로세스 종료'': 한 스레드의 문제로 전체 프로세스가 종료될 수 있다.
스레드는 동일한 주소 공간을 공유하므로 데이터 교환이 쉽지만, 경쟁 조건에 취약하다. 이를 방지하기 위해 뮤텍스 등의 동기화 프리미티브를 사용하여 데이터 구조를 잠금할 수 있다. 하지만 이는 성능 저하를 유발할 수 있다. 다른 동기화 API로는 조건 변수, 임계 구역, 세마포어, 모니터 등이 있다.
멀티스레드 애플리케이션의 주요 단점
- ''동기화 복잡성 및 관련 버그'': 경쟁 조건 등을 피하기 위해 주의해야 한다. 부주의한 사용은 교착 상태 등을 유발할 수 있다.[15]
- ''테스트 불가능성'': 비결정적인 동작으로 인해 테스트가 어렵다.[14][15]
- ''동기화 비용'': 잦은 스레드 간 동기화는 성능 저하를 유발할 수 있다.[16]
한국의 IT 환경에서는 자원 효율성이 중요하므로, 과도한 멀티스레딩은 시스템 자원 낭비를 초래할 수 있다는 점을 고려해야 한다.
참조
[1]
논문
How to Make a Multiprocessor Computer That Correctly Executes Multiprocess Programs
http://research.micr[...]
1979-09
[2]
서적
Modern Operating Systems
Prentice-Hall International Editions
1992
[3]
학위논문
Traffic Control in a Multiplexed Computer System
http://web.mit.edu/S[...]
1966-07
[4]
논문
The Free Lunch Is Over: A Fundamental Turn Toward Concurrency in Software
http://gotw.ca/publi[...]
2005-03
[5]
웹사이트
Erlang: 3.1 Processes
http://www.erlang.or[...]
[6]
AV media
Eight Ways to Handle Non-blocking Returns in Message-passing Programs: from C++98 via C++11 to C++20
https://www.youtube.[...]
CPPCON
2020-11-24
[7]
학회
Enhancing MPI+OpenMP Task Based Applications for Heterogeneous Architectures with GPU support
https://hal-cea.arch[...]
2022-09
[8]
학회
BOLT: Optimizing OpenMP Parallel Regions with User-Level Threads
https://www.mcs.anl.[...]
[9]
서적
Operating system concepts
Wiley
[10]
웹사이트
Multithreading in the Solaris Operating Environment
http://www.sun.com/s[...]
[11]
서적
Murach's CICS for the COBOL Programmer
https://books.google[...]
Mike Murach & Associates
2001
[12]
서적
ALGOL-like languages
https://books.google[...]
Birkhäuser Verlag
1997
[13]
논문
Single-Threading: Back to the Future?
http://accu.org/inde[...]
ACCU
2010-08
[14]
논문
Multi-threading at Business-logic Level is Considered Harmful
https://accu.org/jou[...]
ACCU
2015-08
[15]
웹사이트
The Problem with Threads
http://www.eecs.berk[...]
UC Berkeley
2006-01-10
[16]
웹사이트
Operation Costs in CPU Clock Cycles
http://ithare.com/in[...]
2016-09-12
[17]
웹사이트
スレッドとは - IT用語辞典
https://e-words.jp/w[...]
2022-06-15
[18]
웹사이트
マルチスレッドの基本概念 (マルチスレッドのプログラミング)
https://docs.oracle.[...]
Oracle
[19]
웹사이트
仮想アドレス(論理アドレス)とは - 意味をわかりやすく - IT用語辞典 e-Words
https://e-words.jp/w[...]
[20]
웹사이트
CreateThread function (processthreadsapi.h)
https://docs.microso[...]
Microsoft Docs
[21]
웹사이트
Virtual memory in 32-bit version of Windows - Windows Server
https://learn.micros[...]
Microsoft Learn
[22]
웹사이트
Pushing the Limits of Windows: Processes and Threads - Microsoft Community Hub
https://techcommunit[...]
[23]
웹사이트
スレッドサポートライブラリ - cppreference.com
https://ja.cpprefere[...]
본 사이트는 AI가 위키백과와 뉴스 기사,정부 간행물,학술 논문등을 바탕으로 정보를 가공하여 제공하는 백과사전형 서비스입니다.
모든 문서는 AI에 의해 자동 생성되며, CC BY-SA 4.0 라이선스에 따라 이용할 수 있습니다.
하지만, 위키백과나 뉴스 기사 자체에 오류, 부정확한 정보, 또는 가짜 뉴스가 포함될 수 있으며, AI는 이러한 내용을 완벽하게 걸러내지 못할 수 있습니다.
따라서 제공되는 정보에 일부 오류나 편향이 있을 수 있으므로, 중요한 정보는 반드시 다른 출처를 통해 교차 검증하시기 바랍니다.
문의하기 : help@durumis.com